// Copyright (c) 2021 Presto Labs Pte. Ltd.
// Author: hyuan

#pragma once

#include "coin2/exchange/base/order/rate_config.h"
#include "coin2/exchange/base/order/rate_tracker.h"

namespace coin2::exchange::bybit::api_util {

enum QueryType : int {
  UNKNOWN = 0,
  ACCOUNT_BALANCE = 1,
  OPEN_ORDER = 2,
  HIST_ORDER = 3,
  FILL_ORDER = 4
};

struct RateLimitInfo {
  explicit RateLimitInfo(int32_t rl) : rate_limit(rl) {}
  std::string DebugString() const {
    std::stringstream ss;
    ss << "RateLimitInfo: \n";
    ss << "rate_limit: " << rate_limit << "\n";
    ss << "rate_limit_consumed: " << rate_limit_consumed << "\n";
    ss << "rate_limit_remain: " << rate_limit_remain << "\n";
    ss << "enable_time: " << enable_time << "\n";
    ss << "success : " << success << "\n";
    return ss.str();
  }

  int32_t rate_limit{0};
  int32_t rate_limit_consumed{0};
  int32_t rate_limit_remain{0};
  int64_t enable_time{0};
  bool success{false};
};

class BybitRateTracker : public coin2::exchange::base::order::IRateTracker {
 public:
  using ActionType = coin2::exchange::base::order::IRateTracker::ActionType;
  using RateLimitReportWriter = coin2::exchange::base::order::RateLimitReportWriter;
  using FixedWindowRateTracker = coin2::exchange::base::order::FixedWindowRateTracker;
  using RateConfig = coin2::exchange::base::order::RateConfig;
  using RateLimitInfoSpotBybit = coin2::exchange::order::RateLimitInfoSpotBybit;

 public:
  explicit BybitRateTracker(
      const MarketExchangeApi& mea,
      const coin2::exchange::order::OrderConnectionConfig& cfg);

  void SetRateLimitInfo(int32_t rate_limit, int32_t rate_limit_remaining) override { CHECK(false); }
  void Push(int32_t num_reqs, int64_t timestamp, ActionType action_type) override {}
  bool HasEnoughRateLimit(int64_t timestamp, ActionType action_type) override { return true; };
  void ParseRateLimitInfo(const std::shared_ptr<AsioHttpContext>&, ActionType) override {}
  void SetRateLimitInfoString(const google::protobuf::Message* message) override;
  void WriteRateLimitReportMessage(OrderLoggerCB callback) override;
  double GetMaxRateUsedRatio(int64_t timestamp, ActionType action_type) override;

  bool HasEnoughRateLimit(int64_t timestamp, ActionType action_type, QueryType query_type);
  void Push(int32_t num_reqs, int64_t timestamp, ActionType action_type, QueryType query_type);

 private:
  void InitRateTracker(RateLimitInfoSpotBybit* rate_config, std::string_view mea_str,
      std::string_view example);

  double GetMaxRateUsedRatio(
      const std::unique_ptr<FixedWindowRateTracker>& rate_tracker,
      int64_t timestamp) {
    double rate_used_ratios = 0.0;
    if (rate_tracker) {
      auto max_reqs = rate_tracker->MaxReq();
      auto remaining = rate_tracker->GetRemaining(timestamp);
      if (max_reqs > 0) {
        rate_used_ratios = 1.0 * (max_reqs - remaining) / max_reqs;
      }
    }
    return rate_used_ratios;
  }

 private:
  std::unique_ptr<FixedWindowRateTracker> rate_tracker_getmethod_;
  std::unique_ptr<FixedWindowRateTracker> rate_tracker_postmethod_;
  std::unique_ptr<FixedWindowRateTracker> rate_tracker_balance_;
  std::unique_ptr<FixedWindowRateTracker> rate_tracker_openorder_;
  std::unique_ptr<FixedWindowRateTracker> rate_tracker_historder_;
  std::unique_ptr<FixedWindowRateTracker> rate_tracker_fillorder_;
  std::unique_ptr<FixedWindowRateTracker> rate_tracker_placeorder_;
  std::unique_ptr<FixedWindowRateTracker> rate_tracker_cancelorder_;
  std::unique_ptr<RateLimitReportWriter> rate_limit_report_writer_ = {};
};

}  // namespace coin2::exchange::bybit::api_util
